/*
	This header file serves templated functions to:
	-image-sort - proposed by Isaac & Singleton in "sorting by adress calculation"
	-convert images from TEMPLATE to <float, short, unsigned char>
	-extract histogram from TEMPLATE image with sinsible parameter for 'bin-size'
*/

#ifndef __SUPPORT_H__
#define __SUPPORT_H__

#include <vector>     // for vector of pixel in histogram and fast-image-sort
#include <assert.h>   // do not work on empty images
#include <limits.h>	  // for templating maximum of datatype

#include "Image.H"
#include "Util.H"
#include "IO.H"

#include "ip.h"       // functions served by the ImageProcessing lectures - needed for pixel-class



//putting namespaces in header files might not be the best solution
using namespace std;
using namespace pip;


// this function should not be needed!
// However, watershed runs several times faster if the datatype is smaller.
// For debugging and testing issues this function is still here. 
// It was created to convert an image when passing it as an argument.
template <class T>  
pip::Image<unsigned char> convertAllToUnsignedChar(const pip::Image<T> &input) 
{
  pip::Image<unsigned char> output;  // construct return image
  output.resize(input.dimension());  //resize it into desired size
  T min, max;
  getMinMaxImage(input, min, max);   // get min and max value

  if (min == max) {  //if the image is blanc ... transform it into a blanc image
    output.fill(128); // 128 could be replaced by any number. but 128 gives signal to user 'not zero'
    return output;
  }
  
  float scale( float(numeric_limits<unsigned char>::max()) / (max - min));  // else: use scale available ...
  for (int z = 0; z < input.dim(2); ++z) {
    for (int y = 0; y < input.dim(1); ++y) {
      for (int x = 0; x < input.dim(0); ++x) {
        output(x, y, z) = (unsigned char)(scale * (input(x, y, z) - min));  // and construct result ...
      }
    }
  }
  return output;    // and finally return them
}

// convert any image into an float image
template <class T>
void convertAllToFloat(const pip::Image<T> &input,pip::Image<float> &output) // different type of signature: two arguments
{
  output.resize(input.dimension());   // resize output image

  for (int z = 0; z < input.dim(2); ++z) {
    for (int y = 0; y < input.dim(1); ++y) {
      for (int x = 0; x < input.dim(0); ++x) {
        output(x, y, z) = (float)input(x, y, z); // well, float should handle all other datatypes served by pip ...
      }
    }
  }
}

// same here for short
template <class T>
void convertAllToShort(const Image<T> &input, Image<short> &output)
{
  T min, max;
  getMinMaxImage(input, min, max);

  output.resize(input.dimension());   // prepare for filling

  if (min == max) {
    output.fill(128);
    return;
  }
  
  float scale( float(numeric_limits<short>::max()) / (max - min));
  for (int z = 0; z < input.dim(2); ++z) {
    for (int y = 0; y < input.dim(1); ++y) {
      for (int x = 0; x < input.dim(0); ++x) {
        output(x, y, z) = (short)(scale * (input(x, y, z) - min));  // use available space to fill output image
      }
    }
  }
}


// this is a histogram function (only considering range min max of image, not datatype)
// it is possible to
// ! calculate a cummulative histogram ! 
// as well. see bool argument
template <typename T>
void getHistogram(const Image<T> &image, vector<unsigned int> &vHist, bool cum=false) // input image will be extracted into vector
{																				      // if cum==false: as             histogram
    if (image.empty()) return;														  // if cum==true : as cummulative histogram
    
	unsigned int pos(0);      // pos will avoid recalculations 
	T min(0),max(0);

	getMinMaxImage(image,min,max);

    vHist.clear();                               // prepare hiostogram
    vHist.resize(unsigned int(abs(max-min)+1));       //only for values, that actually occur
    
	for (int z = 0; z < image.dim(2); ++z) {
		for (int y = 0; y < image.dim(1); ++y) {
            for (int x = 0; x < image.dim(0); ++x) {
					vHist[abs(image(x,y)-min)]++;        // count frequency
            }
		}
    }
	if(cum){
		for (unsigned int i = 1; i < vHist.size(); ++i){  // if cummulative histogram requested, compute it
            vHist[i] = vHist[i - 1] + vHist[i];           // by incrementaly adding the values
		}
	}
}


// this function sorts an image in an vector of length "number_levels"
// this is very important for speed of the algorithm, if the datatype has a huge range:
//    without this tweak, an image takes 50 times longer for <short> compared to <char>
//	  now the time is dependent on number_levels... and with number_levels around 250 as fast as with <char> images

// input is the image to be sorted
// in_what_bin will give an mapping from pixel to bin in levels vector
// levels is a vector with all pixels of image, ordered increasingly by value
template <typename T>
void FIsort(const pip::Image<T> &image, pip::Image<float> &in_what_bin, std::vector<std::vector<Pixel> > &levels){
	assert(image.dim(0)-1);									//dont work on empty images

	T min(0),max(0);										// only use values, which are actually used
	getMinMaxImage(image,min,max);
	vector<unsigned int> vCumHist;							// determine bin-size by the cumulative histogram 
	//same number of pixel per bin!
	// init
	unsigned int number_levels(250),curr_bin(0), allpixel(image.dim(0)*image.dim(1)*image.dim(2)); 
	unsigned int pixel_pro_level(allpixel/250);
	levels.resize(number_levels);
	// get the cum_hist
	getHistogram(image, vCumHist, true);
	
	for(int z=0; z<image.dim(2);++z){
		for(int y=0; y<image.dim(1);++y){
			for(int x=0; x<image.dim(0);++x){
				curr_bin=unsigned int(vCumHist[abs(image(x,y,z))-min]/pixel_pro_level); // set every pixel in its bin
				if(curr_bin < 0)  // avoid any 'of by one' error ... just in case of strange errors
					curr_bin=0;
				if(curr_bin > number_levels-1)
					curr_bin=number_levels-1;
				levels[curr_bin].push_back(Pixel(x,y,z));                               // actual work
				// important!
				in_what_bin(x,y,z)=float(curr_bin);									    // remember where which pixel ended up
			}
		}
	}
}
#endif